#include "Emitter.h"
#include "Particles.h"
#include "ParticleSystem.h"
#include "Game.h"
#include "../SGD Wrappers//SGD_Geometry.h"
#include "../SGD Wrappers/SGD_InputManager.h"
#include "StaticObject.h"

Emitter::Emitter(Emitter* savedEmitter)
{
	// is the emitter looping
	isLooping = savedEmitter->isLooping;
	// is the emitter attached to something
	isAttached = savedEmitter->isAttached;

	// current image number
	imageID = savedEmitter->imageID;
	// type of emitter style (point, rectangle, circle, line)
	emitType = savedEmitter->emitType;
	// maximum number of particles on the screen
	maxNumParticles = savedEmitter->maxNumParticles;
	// how many particles are currently active / on the screen
	activeCount = savedEmitter->activeCount;
	moveDirectionX1 = savedEmitter->moveDirectionX1;
	moveDirectionY1 = savedEmitter->moveDirectionY1;
	moveDirectionX2 = savedEmitter->moveDirectionX2;
	moveDirectionY2 = savedEmitter->moveDirectionY2;
	imageSizeX = savedEmitter->imageSizeX;
	imageSizeY = savedEmitter->imageSizeY;

	// rotation angle for the line emitter type
	lineRotation = savedEmitter->lineRotation;
	// starting rotation angle
	startRotation = savedEmitter->startRotation;
	// ending rotation angle
	endRotation = savedEmitter->endRotation;
	// how fast to spawn new particles
	spawnRate = savedEmitter->spawnRate;
	// timer to keep track of spawn rate
	spawnTimer = savedEmitter->spawnTimer;
	// maximum time a particle will live
	maxParticleAge = savedEmitter->maxParticleAge;
	moveDirectionXTimer = savedEmitter->moveDirectionXTimer;
	moveDirectionYTimer = savedEmitter->moveDirectionYTimer;
	maxEmitterLife = savedEmitter->maxEmitterLife;

	position = savedEmitter->position; // position of the emitter
	startVelocity = savedEmitter->startVelocity; // starting velocity
	endVelocity = savedEmitter->endVelocity; // ending velocity
	startScale = savedEmitter->startScale; // starting scale
	endScale = savedEmitter->endScale; // ending scale
	circleVector = savedEmitter->circleVector; // radius used for the circle emitter type
	lineVector = savedEmitter->lineVector; // radius used for the line emitter type

	emitterRectSize = savedEmitter->emitterRectSize; // SGD::Size of the rectangle emitter type

	objectOwner = savedEmitter->objectOwner;

	startColor = savedEmitter->startColor; // starting color
	endColor = savedEmitter->endColor;// ending color

	imagePath = "resource/Graphics/";
	string newImagePath = imagePath + savedEmitter->imageName;

	particleImage = SGD::GraphicsManager::GetInstance()->LoadTexture(newImagePath.c_str());
	image = savedEmitter->image;
}

Emitter::~Emitter()
{
	while (!particleList.empty())
	{
		Particles* p = particleList.back();
		particleList.pop_back();
		delete p;
	}

	SGD::GraphicsManager::GetInstance()->UnloadTexture(particleImage);
}

// Update the emitter
void Emitter::Update(float elapsedTime)
{
	// if the emitter is gone, dont update it.
	if (this == nullptr) return;

	maxEmitterLife -= elapsedTime;

	// keep particles from following the emitter when it moves on the X-axis
	if (moveDirectionX1 == moveDirectionX2 && moveDirectionXTimer > 0 && moveDirectionX1 != 0)
	{
		moveDirectionXTimer -= elapsedTime;
	}
	else if (moveDirectionX1 == moveDirectionX2 && moveDirectionXTimer < 0)
	{
		moveDirectionXTimer = .2f;
		moveDirectionX1 = moveDirectionX2 = 0;
	}
	else if (moveDirectionX1 != moveDirectionX2)
		moveDirectionX2 = moveDirectionX1;

	// keep particles from following the emitter when it moves on the Y-axis
	if (moveDirectionY1 == moveDirectionY2 && moveDirectionYTimer > 0 && moveDirectionY1 != 0)
	{
		moveDirectionYTimer -= elapsedTime;
	}
	else if (moveDirectionY1 == moveDirectionY2 && moveDirectionYTimer < 0)
	{
		moveDirectionYTimer = .2f;
		moveDirectionY1 = moveDirectionY2 = 0;
	}
	else if (moveDirectionY1 != moveDirectionY2)
		moveDirectionY2 = moveDirectionY1;

	// if max number of particles was increased, add more to the list
	if (particleList.size() < unsigned int(maxNumParticles))
		particleList.push_back(new Particles(this));

	// run spawnTimer
	if (spawnTimer >= 0)
		spawnTimer -= elapsedTime;

	if (isAttached)
	{
		SGD::Point tempPosition;
		tempPosition.x = objectOwner->GetPosition().x - Game::GetInstance()->GetCameraPos().x;
		tempPosition.y = objectOwner->GetPosition().y - Game::GetInstance()->GetCameraPos().y;

		if (dynamic_cast<StaticObject*>(objectOwner))
			position = SGD::Point{ tempPosition.x - objectOwner->GetSize().width * 1.5f, tempPosition.y - objectOwner->GetSize().height * 2 };
		else
			position = tempPosition - SGD::Size{ objectOwner->GetSize().width * 3, objectOwner->GetSize().height * 4 };
	}

	// For each particle in particleList
	for (unsigned int i = 0; i < particleList.size(); i++)
	{
		// deactivate particle if dead (will deactivate ALL dead)
		if (particleList[i]->currAge > particleList[i]->maxAge && particleList[i]->isActive)
		{
			particleList[i] = Respawn(particleList[i]);

			// deactivate the particle (so it wont render)
			particleList[i]->isActive = false;
			// reduce the count of active particles
			activeCount--;
		}

		// update each live particle
		if (particleList[i]->isActive)
			particleList[i]->Update(elapsedTime);

		// respawn an inactive particle (will respawn ONE particle - see Respawn)
		if (particleList[i]->isActive == false && spawnTimer < 0 && activeCount < maxNumParticles && isLooping)
		{
			// run Respawn() and replace old particle with new one
			particleList[i] = Respawn(particleList[i]);
			// increase number of active particles
			activeCount++;
		}
	}
}

// render the particles
void Emitter::Render()
{
	if (!this) return;
	if (objectOwner != nullptr)
	{
		// For each particle in the live list
		for (unsigned i = 0; i < particleList.size(); i++)
		{
			if (particleList[i]->isActive)
			{
				// used for code reduction and simplicity
				float scaleX = particleList[i]->currScale.width;
				float scaleY = particleList[i]->currScale.height;
				SGD::Vector rotCenter = SGD::Vector((float)(imageSizeX / 2), (float)(imageSizeY / 2));

				// offset position by image size
				SGD::Point tempPosition = particleList[i]->position;

				SGD::GraphicsManager::GetInstance()->DrawTexture(
					particleImage, // what to render
					particleList[i]->position, // where to render it
					particleList[i]->currRotation, // how much to rotate it
					rotCenter, // center of rotation (image offset)
					particleList[i]->currColor // current color
					);
			}
		}
	}
}

// Respawn a particle
Particles* Emitter::Respawn(Particles* who)
{
	// create a temporary particle to modify
	Particles* tempParticle = who;

	// create temp variable for particle location
	SGD::Point tempParticleLocation = position;
	// create temp variable for particle velocity
	SGD::Vector tempVel = tempParticle->startVelocity;

	// switch between emitter types
	switch (emitType)
	{
#pragma region
	case 0: // point
		// nothing here, tempRectLocation = position (already set)
		break;
	case 1: // rectangle
	{
#pragma region
				// create a temp rect from xml / default values
				SGD::Rectangle tempRect = SGD::Rectangle(
					position.x - emitterRectSize.width / 2,
					position.y - emitterRectSize.height / 2,
					emitterRectSize.width,
					emitterRectSize.height
					);

				// set a random spawn point within the temp rect
				tempParticleLocation = SGD::Point(
					(float)(rand() % (int)tempRect.right - (int)tempRect.left),
					(float)(rand() % (int)tempRect.bottom - (int)tempRect.top)
					);
#pragma endregion
	}
		break;
	case 2: // circle
	{
#pragma region
				// create a 2D offSet vector for radius
				SGD::Vector circleOffset = circleVector;

				// random angle (in degrees)
				int randDegree = rand() % 360;
				// convert from degrees to radians
				float radians = randDegree * (SGD::PI / 180);

				// roate the vector
				circleOffset.Rotate(radians);

				// randomize the offSets' position on the line (radius)
				circleOffset *= float((rand() % 10) / 9.0f);

				// offset the particles spawn position
				tempParticleLocation += circleOffset;
#pragma endregion
	}
		break;
	case 3: // line
	{
#pragma region
				// create an offSet vector for radius
				SGD::Vector offSet = lineVector;

				// rotate the vector by radians angle
				offSet.Rotate(lineRotation * (SGD::PI / 180));

				// randomize the offSets position on the line
				offSet *= float((rand() % 10) / 9);

				// offset the particles spawn position
				tempParticleLocation += offSet;
#pragma endregion
	}
		break;
#pragma endregion
	}

	// reset particle variables
	{
#pragma region

		// create tempParticle age and randomize it from the original
		float tempParticleAge = ((rand() % 10) / 9.0f) * maxParticleAge;


		// randomize tempVelocity within startVelocity Range
		tempVel = SGD::Vector((float)(rand() % (int)startVelocity.x), (float)(rand() % (int)startVelocity.y));

		tempVel.x *= float((rand() % 10) / 9.0f);
		tempVel.y *= float((rand() % 10) / 9.0f);

		// startVelocity rotation
#pragma region
		// create a 2D offSet vector for radius
		SGD::Vector velOffset = tempVel;

		// random angle (in degrees)
		int randDegree = rand() % 360;
		// convert from degrees to radians
		float radians = randDegree * (SGD::PI / 180);

		// roate the vector
		velOffset.Rotate(radians);

		// scale the vector
		velOffset *= float((rand() % 10) / 9.0f);

		// reset the temp particle velocity
		tempVel = velOffset;
#pragma endregion

		// endVelocity rotation
#pragma region
		// create a 2D offSet vector for radius
		velOffset = endVelocity;

		// rotate the 3D vector
		velOffset.Rotate(radians);

		SGD::Vector tempEndVel = velOffset;
#pragma endregion

		// keep particles from "crossing over" emitter (especially with rectangle and circle emitter types)
#pragma region
		if (tempParticleLocation.x < position.x && tempVel.x > 0)
			tempVel.x = -tempVel.x;
		if (tempParticleLocation.x > position.x && tempVel.x < 0)
			tempVel.x = -tempVel.x;
		if (tempParticleLocation.y < position.y && tempVel.y > 0)
			tempVel.y = -tempVel.y;
		if (tempParticleLocation.y > position.y && tempVel.y < 0)
			tempVel.y = -tempVel.y;
#pragma endregion

		// re-assign new variables (must set since reactivating not creating NEW)
#pragma region
		// BOOL
		tempParticle->isActive = true; // reactivate the particle

		// FLOAT
		tempParticle->maxAge = tempParticleAge; // reset maxAge
		tempParticle->currAge = 0; // restart current age

		tempParticle->startRotation = startRotation; // reset startRotation
		tempParticle->currRotation = startRotation; // reset currRotation
		tempParticle->endRotation = endRotation; // reset endRotation

		// SGD::Vector
		tempParticle->position = tempParticleLocation; // new position based on emitter type

		tempParticle->startVelocity = tempVel; // reset startVelocity to newest value
		tempParticle->currVelocity = tempVel; // new random velocity
		tempParticle->endVelocity = tempEndVel; // reset endVelocity to newest value

		tempParticle->startScale = startScale; // reset startScale to newest value
		tempParticle->currScale = startScale; // reset currScale
		tempParticle->endScale = endScale; // reset endScale to newest value

		tempParticle->startColor = startColor; // reset startColor to newest value
		tempParticle->currColor = startColor; // reset current color
		tempParticle->endColor = endColor; // reset endColor to newest value
		tempParticle->currA = startColor.alpha; // reset current Alpha color
		tempParticle->currR = startColor.red; // reset current Red color
		tempParticle->currG = startColor.green; // reset current Green color
		tempParticle->currB = startColor.blue; // reset current Blue color
#pragma endregion

#pragma endregion
	}

	// return the new particle
	return tempParticle;
}
